/*
 * Copyright (c) 2020 - present, Inspur Genersoft Co., Ltd.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.iec.edp.caf.data.source;

import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLDecoder;
import java.net.URLEncoder;

/**
 * Utility class that allows expressing URIs in alternative forms: individual fields or a URI string
 *
 * @author Ramnivas Laddad
 * @author Scott Frederick
 */
public class UriInfo {

    private String scheme;
    private String host;
    private int port;
    private String userName;
    private String password;
    private String path;
    private String query;
    private String schemeSpecificPart;

    private String uriString;

    public UriInfo(String scheme, String host, int port, String username, String password) {
        this(scheme, host, port, username, password, null, null);
    }

    public UriInfo(String scheme, String host, int port, String username, String password, String path) {
        this(scheme, host, port, username, password, path, null);
    }

    public UriInfo(String scheme, String host, int port, String username, String password, String path, String query) {
        this.scheme = scheme;
        this.host = host;
        this.port = port;
        this.userName = username;
        this.password = password;
        this.path = path;
        this.query = query;

        this.uriString = buildUri().toString();
    }

    public UriInfo(String uriString) {
        this.uriString = uriString;

        URI uri = getUri();
        this.scheme = uri.getScheme();
        this.host = uri.getHost();
        this.port = uri.getPort();
        this.path = parsePath(uri);
        this.query = uri.getQuery();
        this.schemeSpecificPart = uri.getSchemeSpecificPart();

        String[] userinfo = parseUserinfo(uri);
        this.userName = uriDecode(userinfo[0]);
        this.password = uriDecode(userinfo[1]);
    }

    public String getScheme() {
        return scheme;
    }

    public String getHost() {
        return host;
    }

    public int getPort() {
        return port;
    }

    public String getUserName() {
        return userName;
    }

    public String getPassword() {
        return password;
    }

    public String getPath() {
        return path;
    }

    public String getQuery() {
        return query;
    }

    public String getSchemeSpecificPart() {
        return schemeSpecificPart;
    }

    public URI getUri() {
        try {
            return new URI(uriString);
        } catch (URISyntaxException e) {
            throw new IllegalArgumentException("Invalid URI " + uriString, e);
        }
    }

    public String getUriString() {
        return uriString;
    }

    private URI buildUri() {
        String userInfo = null;

        if (userName != null && password != null) {
            userInfo = userName + ":" + password;
        }

        String cleanedPath = path == null || path.startsWith("/") ? path : "/" + path;

        try {
            return new URI(scheme, userInfo, host, port, cleanedPath, query, null);
        } catch (URISyntaxException e) {
            String details = String.format("Error creating URI with components: " +
                            "scheme=%s, userInfo=%s, host=%s, port=%d, path=%s, query=%s",
                    scheme, userInfo, host, port, cleanedPath, query);
            throw new IllegalArgumentException(details, e);
        }
    }

    private String[] parseUserinfo(URI uri) {
        String userInfo = uri.getRawUserInfo();

        if (userInfo != null) {
            String[] userPass = userInfo.split(":");
            if (userPass.length == 1) {
                return new String[]{userPass[0], null};
            } else {
                int pos = userInfo.indexOf(":");  // 找分隔符的位置
                return new String[]{
                        userInfo.substring(0, pos),// 找到分隔符，截取子字符串
                        userInfo.substring(pos + 1)// 剩下需要处理的字符串
                };
            }
        }
        return new String[]{null, null};
    }

    private String parsePath(URI uri) {
        String rawPath = uri.getRawPath();
        if (rawPath != null && rawPath.length() > 1) {
            return rawPath.substring(1);
        }
        return null;
    }

    private static String uriDecode(String s) {
        if (s == null) {
            return null;
        }

        try {
            // URLDecode decodes '+' to a space, as for
            // form encoding. So protect plus signs.
            return URLDecoder.decode(s.replace("+", "%2B"), "UTF-8");
        } catch (UnsupportedEncodingException e) {
            // US-ASCII is always supported
            throw new RuntimeException(e);
        }
    }

    public static String urlEncode(String s) {
        try {
            return URLEncoder.encode(s, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public String toString() {
        return uriString;
    }
}
